/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 draw_adventure_tile.h

	$Header: /heroes4/draw_adventure_tile.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( DRAW_ADVENTURE_TILE_H_INCLUDED )
#define DRAW_ADVENTURE_TILE_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <algorithm>
#include <cassert>
#include <vector>

#include "abstract_bitmap.h"
#include "adventure_tile_size.h"
#include "quad.h"
#include "screen_point.h"
#include "sized_int_types.h"

class t_adventure_tile_texture;
class t_transition_mask;

// ------------------------------------------------------------------------------
// t_composite_tile_texture class
// ------------------------------------------------------------------------------

class t_composite_tile_texture
{
public:
	// Constructor
	t_composite_tile_texture();

	// Member functions
	t_uint16 *			get_column_ptr( int column );
	t_uint16 const *	get_column_ptr( int column ) const;

private:
	// Types
	typedef int t_column_offset_array[ k_adventure_tile_width ];

	// Static member functions
	static t_column_offset_array const & get_column_offset_array();
	static int get_column_offset( int column );

	// Data members
	std::vector< t_uint16 >	m_data;
};

// ------------------------------------------------------------------------------
// t_composite_tile_texture inline members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline int t_composite_tile_texture::get_column_offset( int column )
{
	assert( column >= 0 && column < k_adventure_tile_width );
	static t_column_offset_array const & column_offset_array = get_column_offset_array();
	return column_offset_array[ column ];
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline t_uint16 * t_composite_tile_texture::get_column_ptr( int column )
{
	return &m_data[ get_column_offset( column ) ];
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline t_uint16 const * t_composite_tile_texture::get_column_ptr( int column ) const
{
	return &m_data[ get_column_offset( column ) ];
}

// ------------------------------------------------------------------------------
// t_composite_transition_mask class
// ------------------------------------------------------------------------------

class t_composite_transition_mask
{
public:
	// Constructor
	t_composite_transition_mask();

	// Member functions
	t_uint32 &	get_column_mask( int column );
	t_uint32	get_column_mask( int column ) const;
	void		exclude( t_transition_mask const & other );

	// Operators
	t_composite_transition_mask & operator|=( t_transition_mask const & other );
	t_composite_transition_mask & operator&=( t_transition_mask const & other );

private:
	// Data members
	t_uint32	m_data[ k_adventure_tile_width ];
};

// ------------------------------------------------------------------------------
// t_composite_transition_mask inline members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline t_composite_transition_mask::t_composite_transition_mask()
{
	std::fill_n( m_data, k_adventure_tile_width, ~t_uint32( 0 ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline t_uint32 & t_composite_transition_mask::get_column_mask( int column )
{
	assert( column >= 0 && column < k_adventure_tile_width );
	return m_data[ column ];
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
inline t_uint32 t_composite_transition_mask::get_column_mask( int column ) const
{
	assert( column >= 0 && column < k_adventure_tile_width );
	return m_data[ column ];
}

// ------------------------------------------------------------------------------
// Non-member functions
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_uint16 apply_lighting( t_uint16 color, int lighting, bool x_odd, bool y_odd );

// ------------------------------------------------------------------------------
// Draw the specified section of the adventure tile texture to a composite
// buffer.
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_adventure_tile_texture const &	src,
	t_uint16 const *					palette,
	t_composite_tile_texture &			dest,
	int									left,
	int									right );

// ------------------------------------------------------------------------------
// Draw the specified section of the adventure tile texture to a composite
// buffer using a transition mask
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_adventure_tile_texture const &	src,
	t_uint16 const *					palette,
	t_composite_tile_texture &			dest,
	int									left,
	int									right,
	t_transition_mask const &			mask );

// ------------------------------------------------------------------------------
// Draw the specified section of one composite buffer to another composite
// buffer using a transition mask
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_composite_tile_texture const &	src,
	t_composite_tile_texture &			dest,
	int									left,
	int									right,
	t_transition_mask const &			mask );

// ------------------------------------------------------------------------------
// Draw the specified adventure tile texture to a bitmap
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_adventure_tile_texture const &	src,
	t_uint16 const *					palette,
	t_abstract_bitmap16 &				dest,
	t_screen_point const &				dest_pos,
	t_screen_rect const &				clip_rect,
	t_quad< int > const &				height_quad,
	t_quad< int > const &				lighting_quad );

// ------------------------------------------------------------------------------
// draw an adventure tile using a transition mask
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_adventure_tile_texture const &	src,
	t_uint16 const *					palette,
	t_abstract_bitmap16 &				dest,
	t_screen_point const &				dest_pos,
	t_screen_rect const &				clip_rect,
	t_quad< int > const &				height_quad,
	t_quad< int > const &				lighting_quad,
	t_transition_mask const &			mask );

// ------------------------------------------------------------------------------
// draw transition mask, from a composite buffer.  Assumes a 4 bit alpha blending mask.
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_composite_tile_texture const &	src,
	t_abstract_bitmap16 &				dest,
	t_screen_point const &				dest_pos,
	t_screen_rect const &				clip_rect,
	t_quad< int > const &				height_quad,
	t_quad< int > const &				lighting_quad,
	t_transition_mask const &			mask );

// ------------------------------------------------------------------------------
// draw transition mask, from a composite buffer.
// ------------------------------------------------------------------------------
void draw_adventure_tile(
	t_composite_tile_texture const &	src,
	t_abstract_bitmap16 &				dest,
	t_screen_point const &				dest_pos,
	t_screen_rect const &				clip_rect,
	t_quad< int > const &				height_quad,
	t_quad< int > const &				lighting_quad,
	t_composite_transition_mask const &	mask );

// ------------------------------------------------------------------------------
// Fill an adventure tile with a specified color
// ------------------------------------------------------------------------------
template <typename t_pixel>
void fill_adventure_tile(
	t_pixel							color,
	t_abstract_bitmap<t_pixel> &	dest,
	t_screen_point const &			dest_pos,
	t_screen_rect const &			clip_rect,
	t_quad< int > const &			height_quad )
{
	assert( is_normalized( clip_rect ) );

	int left_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.left;
	int top_vertex_dest_y = dest_pos.y - height_quad.top;
	int right_vertex_dest_y = dest_pos.y + k_adventure_tile_height / 2 - height_quad.right;
	int bottom_vertex_dest_y = dest_pos.y + k_adventure_tile_height - height_quad.bottom;

	t_screen_rect dest_rect;

	// Draw the left half of the tile
	dest_rect.left = dest_pos.x;
	dest_rect.top = top_vertex_dest_y < left_vertex_dest_y ? top_vertex_dest_y : left_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.bottom = bottom_vertex_dest_y > left_vertex_dest_y ? bottom_vertex_dest_y : left_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x;
		int src_x_end = dest_rect.right - dest_pos.x;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( left_vertex_dest_y - top_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_y_end = left_vertex_dest_y + ( bottom_vertex_dest_y - left_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int dest_column_top = dest_y_start > dest_rect.top ? dest_y_start : dest_rect.top;

				t_pixel * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_column_top ) + dest_pos.x + src_x;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_column_top;

				while ( dest_pixels_to_go > 0 )
				{
					*dest_ptr = color;
					dest_ptr = dest.advance_line( dest_ptr );
					--dest_pixels_to_go;
				}
			}
		}
	}

	// Draw the right half of the tile
	dest_rect.left = dest_pos.x + k_adventure_tile_width / 2;
	dest_rect.top = top_vertex_dest_y < right_vertex_dest_y ? top_vertex_dest_y : right_vertex_dest_y;
	dest_rect.right = dest_pos.x + k_adventure_tile_width;
	dest_rect.bottom = bottom_vertex_dest_y > right_vertex_dest_y ? bottom_vertex_dest_y : right_vertex_dest_y;
	if ( intersect( dest_rect, clip_rect ) )
	{
		dest_rect = intersection( dest_rect, clip_rect );

		int src_x_start = dest_rect.left - dest_pos.x - k_adventure_tile_width / 2;
		int src_x_end = dest_rect.right - dest_pos.x - k_adventure_tile_width / 2;

		int src_x;
		for ( src_x = src_x_start; src_x < src_x_end; ++src_x )
		{
			int dest_y_start = top_vertex_dest_y + ( right_vertex_dest_y - top_vertex_dest_y ) * src_x / ( k_adventure_tile_width / 2 );
			int dest_y_end = right_vertex_dest_y + ( bottom_vertex_dest_y - right_vertex_dest_y ) * ( k_adventure_tile_width / 2 - src_x - 1 ) / ( k_adventure_tile_width / 2 );
			int dest_delta_y = dest_y_end - dest_y_start;

			if ( dest_y_start < dest_rect.bottom && dest_y_end > dest_rect.top && dest_delta_y > 0 )
			{
				int dest_column_top = dest_y_start > dest_rect.top ? dest_y_start : dest_rect.top;

				t_pixel * dest_ptr = dest.advance_line( dest.get_data_ptr(), dest_column_top ) + dest_pos.x + src_x + k_adventure_tile_width / 2;

				int dest_column_bottom = dest_y_end < dest_rect.bottom ? dest_y_end : dest_rect.bottom;
				int dest_pixels_to_go = dest_column_bottom - dest_column_top;

				while ( dest_pixels_to_go > 0 )
				{
					*dest_ptr = color;
					dest_ptr = dest.advance_line( dest_ptr );
					--dest_pixels_to_go;
				}
			}
		}
	}
}

// ------------------------------------------------------------------------------
// Fill an adventure tile with specified foreground and background alphas and a
// transition mask
// ------------------------------------------------------------------------------
void fill_adventure_tile_alpha_mask(
	t_uint8						forground_alpha,
	t_uint8						background_alpha,
	t_abstract_bitmap8 &		dest,
	t_screen_point const &		dest_pos,
	t_screen_rect const &		clip_rect,
	t_quad< int > const &		height_quad,
	t_transition_mask const &	mask );

// ------------------------------------------------------------------------------
// Compose an adventure tile alpha mask with a specified alpha and a
// transition mask
// ------------------------------------------------------------------------------
void compose_adventure_tile_alpha_mask(
	t_uint8						alpha,
	t_abstract_bitmap8 &		dest,
	t_screen_point const &		dest_pos,
	t_screen_rect const &		clip_rect,
	t_quad< int > const &		height_quad,
	t_transition_mask const &	mask );

// ------------------------------------------------------------------------------
// Shade an adventure tile
// ------------------------------------------------------------------------------
void shade_adventure_tile(
	t_uint16				shade_color,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_pos,
	t_screen_rect const &	clip_rect,
	t_quad< int > const &	height_quad );

// ------------------------------------------------------------------------------
// Shade half an adventure tile
// ------------------------------------------------------------------------------
void shade_adventure_tile_half(
	t_uint16				shade_color,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_pos,
	t_screen_rect const &	clip_rect,
	t_quad< int > const &	height_quad,
	bool					left );

// ------------------------------------------------------------------------------
// Shade a quarter of an adventure tile
// ------------------------------------------------------------------------------
void shade_adventure_tile_quarter(
	t_uint16				shade_color,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_pos,
	t_screen_rect const &	clip_rect,
	t_quad< int > const &	height_quad,
	bool					left,
	bool					top );

#endif // !defined( DRAW_ADVENTURE_TILE_H_INCLUDED )
